﻿Imports System.Drawing.Text
Imports System.IO
Imports System.Text.RegularExpressions
Imports 哈里盯盘.HarryNode


Public Class Form1
    Private clickPos As Point
    Public bindCode As String
    Public api As New EastmoneyAPI()
    Public basePrice As Decimal
    Private miniImg As New Bitmap(1, 1)
    Private Shared miniImgG As Graphics
    Private mustRunning As Boolean
    Public WithEvents Config As New HarryNode("{}")
    Private codeIndexName As New Dictionary(Of String, String)

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        ReadCodeIndexCN()
        ReadConfig()
        bindCode = Config.GetAtt("绑定股票代码", bindCode)
        Dim pos As String = Config.GetAtt("窗体位置", "0,0")
        Left = Val(pos.Split(",")(0))
        Top = Val(pos.Split(",")(1))
        Opacity = Config.GetAtt("窗体透明度", Opacity)
        miniImgG = Graphics.FromImage(miniImg)
        '右键窗体设定股票代码
        'Dim baseNode As New HarryNode("", api.GetDayData(bindCode))
        'Debug.WriteLine(baseNode.ToJson)
        'basePrice = baseNode.GetAtt("data.klines.0").split(",")(1)
        If Not mustRunning AndAlso Not EastmoneyAPI.IsInTradingHours() Then
            DrawOutTrading()
        End If
    End Sub
    Private Sub ReadCodeIndexCN(Optional filePath As String = "codeName.txt")
        Dim content As String = If(File.Exists(filePath),
                               File.ReadAllText(filePath),
                               My.Resources.rawCodeIndexName)

        For Each line As String In content.Split({ControlChars.CrLf, ControlChars.Lf}, StringSplitOptions.RemoveEmptyEntries)
            Dim separator As Char = GetSeparator(line)
            If separator <> Chr(0) Then
                Dim parts() As String = line.Split(separator)
                If parts.Length >= 2 Then
                    codeIndexName.Add(parts(0), parts(1))
                End If
            End If
        Next
    End Sub

    Private Function GetSeparator(line As String) As Char
        If line.Contains(vbTab) Then Return vbTab
        If line.Contains(" ") Then Return " "c
        If line.Contains(",") Then Return ","c
        Return Chr(0) ' 表示无有效分隔符
    End Function
    Private Sub ReadConfig()
        Try
            If File.Exists("config.json") Then
                Config = New HarryNode(File.ReadAllText("config.json"))
            End If
        Catch ex As Exception
            Debug.WriteLine(ex.Message)
        End Try
    End Sub

    Private Sub Form1_MouseDown(sender As Object, e As MouseEventArgs) Handles PictureBox1.MouseDown
        clickPos = e.Location
    End Sub

    Private Sub Form1_MouseUp(sender As Object, e As MouseEventArgs) Handles PictureBox1.MouseUp
        clickPos = New Point
    End Sub

    Private Sub Form1_MouseMove(sender As Object, e As MouseEventArgs) Handles PictureBox1.MouseMove
        If clickPos.X <> 0 OrElse clickPos.Y <> 0 Then
            Left += e.Location.X - clickPos.X
            Top += e.Location.Y - clickPos.Y
        End If
    End Sub

    Private Sub 退出ToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles 退出ToolStripMenuItem.Click
        Close()
    End Sub

    Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
        Dim allSt As New List(Of ShowText)
        Dim textSize As New SizeF
        Dim stSize As SizeF
        If Not mustRunning AndAlso Not EastmoneyAPI.IsInTradingHours() Then
            Return
        End If
        Dim node As HarryNode
        If Config.GetAtt("显示上证", False) Then
            node = New HarryNode(api.GetRealData("000001", Config.GetAtt("UT", ""), "1"))
            allSt.Add(GetShowText(node, "000001", "上证指数"))
            stSize = allSt.Last.GetTextSize(Font)
            textSize.Width = Math.Max(stSize.Width, textSize.Width)
            textSize.Height += stSize.Height
        End If
        If Config.GetAtt("显示深证", False) Then
            node = New HarryNode(api.GetRealData("399001", Config.GetAtt("UT", ""), "0"))
            allSt.Add(GetShowText(node, "399001", "深证指数"))
            stSize = allSt.Last.GetTextSize(Font)
            textSize.Width = Math.Max(stSize.Width, textSize.Width)
            textSize.Height += stSize.Height
        End If
        If Config.GetAtt("显示创业板", False) Then
            node = New HarryNode(api.GetRealData("399006", Config.GetAtt("UT", ""), "0"))
            allSt.Add(GetShowText(node, "399006", "创业板指数"))
            stSize = allSt.Last.GetTextSize(Font)
            textSize.Width = Math.Max(stSize.Width, textSize.Width)
            textSize.Height += stSize.Height
        End If
        For Each code As String In (bindCode & ",").Split(",")
            If code = "" Then
                Continue For
            End If
            allSt.Add(GetCodeShowText(code))
            stSize = allSt.Last.GetTextSize(Font)
            textSize.Width = Math.Max(stSize.Width, textSize.Width)
            textSize.Height += stSize.Height
        Next
        If allSt.Count = 0 Then
            Dim nullText As New ShowText("右键此处选择【设定股票】添加盯盘股票！", Brushes.Yellow)
            allSt.Add(nullText)
            stSize = allSt.Last.GetTextSize(Font)
            textSize.Width = Math.Max(stSize.Width, textSize.Width)
            textSize.Height += stSize.Height
        End If
        Dim img As New Bitmap(CInt(textSize.Width) + 20, CInt(textSize.Height) + 20)
        Dim g As Graphics = Graphics.FromImage(img)
        g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit
        Dim writePos As New PointF(10, 10)
        For Each st As ShowText In allSt
            g.DrawString(st.text, Font, st.setColor, writePos)
            writePos.Y += st.GetTextSize(Font).Height
            If st.brushDispose Then
                st.setColor.Dispose()
            End If
        Next
        PictureBox1.Image?.Dispose()
        PictureBox1.Image = img
        Width = img.Width
        Height = img.Height
    End Sub
    Private Sub DrawOutTrading()
        Dim img As New Bitmap(150, 40)
        Dim g As Graphics = Graphics.FromImage(img)
        g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit
        g.DrawString("不在交易时间段中", Font, Brushes.White, New PointF(10, 10))
        PictureBox1.Image?.Dispose()
        PictureBox1.Image = img
        Width = img.Width
        Height = img.Height
    End Sub
    Class ShowText
        Public text As String
        Public setColor As SolidBrush
        Public costPrice As Decimal
        Public brushDispose As Boolean
        Public Sub New(text As String, setColor As SolidBrush, Optional brushDispose As Boolean = False)
            Me.text = text
            Me.setColor = setColor
            Me.brushDispose = brushDispose
        End Sub
        Public Sub New(text As String, setColor As SolidBrush, costPrice As Decimal)
            Me.text = text
            Me.setColor = setColor
            Me.costPrice = costPrice
        End Sub
        Public Function GetTextSize(useFont As Font) As SizeF
            Return miniImgG.MeasureString(text, useFont)
        End Function
    End Class
    ''' <summary>
    ''' 将输入值 x (-0.1 到 0.1) 线性映射为从亮绿色 (Lime) 到红色 (Red) 的 SolidBrush。
    ''' </summary>
    ''' <param name="x">输入值，应在 [-0.1, 0.1] 范围内</param>
    ''' <returns>对应颜色的 SolidBrush</returns>
    Public Function MapXToLimeToRedBrush(ByVal x As Double) As SolidBrush
        ' 定义输入范围
        Dim minValue As Double = -0.1
        Dim maxValue As Double = 0.1

        ' 确保 x 在指定范围内，进行 clamping (截断)
        x = Math.Max(minValue, Math.Min(maxValue, x))

        ' 将 x 映射到 [0, 1] 的 t 值 (t = 0 对应 minValue, t = 1 对应 maxValue)
        ' 因为 minValue 是 -0.1, maxValue 是 0.1, 所以范围是 0.2
        Dim t As Double = (x - minValue) / (maxValue - minValue) ' t = (x + 0.1) / 0.2

        ' Lime (亮绿色) 的 RGB: R=0, G=255, B=0
        ' Red (红色) 的 RGB:   R=255, G=0, B=0
        ' 我们需要从 Lime 渐变到 Red:
        '   - R 分量: 从 0 增加到 255
        '   - G 分量: 从 255 减少到 0
        '   - B 分量: 保持 0
        Dim red As Integer = CInt(255 * t)         ' t=0 -> 0, t=1 -> 255
        Dim green As Integer = CInt(255 * (1 - t)) ' t=0 -> 255, t=1 -> 0
        Dim blue As Integer = 0                     ' 始终为 0

        ' 创建并返回 SolidBrush
        Return New SolidBrush(Color.FromArgb(red, green, blue))
    End Function
    Private Function GetShowText(node As HarryNode, code As String, name As String) As ShowText
        Dim value As Decimal
        Dim preKPrice As Decimal
        Dim setColor As SolidBrush
        value = CDec(node.GetAtt("data.details.0").split(",")(1))
        preKPrice = CDec(node.GetAtt("data.prePrice"))
        code &= " " & name
        If Config.GetAtt("字色渐变", True) Then
            setColor = MapXToLimeToRedBrush((value - preKPrice) / preKPrice)
            Return New ShowText($"{code} {value} {Math.Round((value - preKPrice) / preKPrice * 100, 2)}%", setColor, True)
        Else
            If value >= preKPrice Then
                setColor = Brushes.Red
            Else
                setColor = Brushes.Lime
            End If
        End If
        Return New ShowText($"{code} {value} {Math.Round((value - preKPrice) / preKPrice * 100, 2)}%", setColor)
    End Function
    Private Function GetCodeShowText(code As String) As ShowText
        Dim node As HarryNode
        Dim value As Decimal
        Dim preKPrice As Decimal
        Dim setColor As SolidBrush
        Try
            If Regex.IsMatch(code, "^\d\d\d\d\d\d$") Then
                node = New HarryNode("", api.GetRealData(code, Config.GetAtt("UT", "")))
                Debug.WriteLine($"node={node.ToJson}")
                value = CDec(node.GetAtt("data.details.0").split(",")(1))
                preKPrice = CDec(node.GetAtt("data.prePrice"))
                If codeIndexName.ContainsKey(code) Then
                    code &= " " & codeIndexName(code)
                End If
                If Config.GetAtt("字色渐变", True) Then
                    setColor = MapXToLimeToRedBrush((value - preKPrice) / preKPrice)
                    Return New ShowText($"{code} {value} {Math.Round((value - preKPrice) / preKPrice * 100, 2)}%", setColor, True)
                Else
                    If value >= preKPrice Then
                        setColor = Brushes.Red
                    Else
                        setColor = Brushes.Lime
                    End If
                End If
                Return New ShowText($"{code} {value} {Math.Round((value - preKPrice) / preKPrice * 100, 2)}%", setColor)
            ElseIf Regex.IsMatch(code, "\d\d\d\d\d\d:\d+\.\d+") Then
                Dim data() As String = code.Split(":")
                node = New HarryNode("", api.GetRealData(data(0), Config.GetAtt("UT", "")))
                value = CDec(node.GetAtt("data.details.0").split(",")(1))
                preKPrice = CDec(data(1))
                If value >= preKPrice Then
                    setColor = Brushes.Red
                Else
                    setColor = Brushes.Lime
                End If
                code = data(0)
                If codeIndexName.ContainsKey(code) Then
                    code &= " " & codeIndexName(code)
                End If
                Return New ShowText($"￥ {code} {value} {Math.Round((value - preKPrice) / preKPrice * 100, 2)}%", setColor)
            End If
            Return New ShowText($"无效格式""{code}""", Brushes.Yellow)
        Catch ex As Exception
            Return New ShowText($"解析错误""{code}"", {ex.Message}", Brushes.OrangeRed)
        End Try
    End Function

    Private Sub 设定股票ToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles 设定股票ToolStripMenuItem.Click
        Dim code As String = InputBox("输入股票代码：", "设定股票代码", bindCode)
        If Regex.IsMatch(code, "^\d{6}(?:\:\d+(?:\.\d+)?)?(?:\,\d{6}(?:\:\d+(?:\.\d+)?)?)*$") Then
            bindCode = code
            Config.SetAtt("绑定股票代码", bindCode, NodeTypeEnum.isString)
        End If
    End Sub

    Private Sub NodeContentChangeLater(path As String, ByRef nowValue As Object, ByRef newValueType As NodeTypeEnum) Handles Config.NodeContentChangeLater
        SaveConfig()
    End Sub

    Private Sub SaveConfig()
        File.WriteAllText("config.json", Config.ToJson)
    End Sub

    Private Sub Label1_Click(sender As Object, e As EventArgs)

    End Sub

    Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
        Config.SetAtt("窗体位置", $"{Left},{Top}", NodeTypeEnum.isString)
    End Sub

    Private Sub Form1_Click(sender As Object, e As EventArgs) Handles Me.Click

    End Sub

    Private Sub 最大小化ToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles 最大小化ToolStripMenuItem.Click
        If Visible Then
            Hide()
            NotifyIcon1.Visible = True
        Else
            Show()
            NotifyIcon1.Visible = False
        End If
    End Sub

    Private Sub 设置ToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles 设置ToolStripMenuItem.Click
        Form2.Show()
    End Sub
End Class
